package com.jy.modules.cims.common.sysUtils;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @description zip压缩、解压(支持中文文件名)
 * @version v1.0
 * @author shengzhoutao
 * @Date 2015-4-27
 */
public class ZipFileUtils {
	private static Logger logger = LoggerFactory.getLogger(ZipFileUtils.class);

	private static int bufSize = 8096; // size of bytes 设置压缩或解压时缓冲区大小，单位字节。

	/**
	 * @description 压缩文件夹内的所有文件和目录
	 * 
	 * @param zipDirectory
	 *            需要压缩的文件夹名
	 * @return 成功返回null，否则返回失败信息
	 */
	public static String zip(String zipDirectory) {
		File zipDir = new File(zipDirectory);
		return zip(zipDirectory, zipDir.getPath(), false);
	}

	/**
	 * @description 压缩文件夹内的所有文件和目录
	 * 
	 * @param zipDirectory
	 *            需要压缩的文件夹名
	 * @param zipFileName
	 *            压缩后的zip文件名，如果后缀不是".zip, .jar, .war"， 自动添加后缀".zip"。
	 * @param includeSelfDir
	 *            是否包含自身文件夹
	 * @return 成功返回null，否则返回失败信息
	 */
	public static String zip(String zipDirectory, String zipFileName, boolean includeSelfDir) {
		File zipDir = new File(zipDirectory);
		File[] willZipFileArr;
		if (includeSelfDir || zipDir.isFile()) {
			willZipFileArr = new File[] { zipDir };
		} else {
			willZipFileArr = zipDir.listFiles();
		}
		return zip(willZipFileArr, zipFileName);
	}

	/**
	 * @description 压缩多个文件或目录。可以指定多个单独的文件或目录。
	 * 
	 * @param files
	 *            要压缩的文件或目录组成的<code>File</code>数组。
	 * @param zipFileName
	 *            压缩后的zip文件名，如果后缀不是".zip, .jar, .war"，自动添加后缀".zip"。
	 * @return 成功返回null，否则返回失败信息
	 */
	public static String zip(File[] files, String zipFileName) {
		if (files == null) {
			return "待压缩的文件不存在。";
		}

		// 未指定压缩文件名，默认为"ZipFile"
		if (zipFileName == null || zipFileName.equals("")) {
			zipFileName = "ZipFile";
		}
		// 添加".zip"后缀
		if (!zipFileName.toLowerCase().endsWith(".zip") && !zipFileName.toLowerCase().endsWith(".jar") && !zipFileName.toLowerCase().endsWith(".war")) {
			zipFileName += ".zip";
		}

		ZipOutputStream zipOutputStream = null;
		try {
			zipOutputStream = new ZipOutputStream(new FileOutputStream(zipFileName));
			for (File file : files) {
				zipFiles(file, zipOutputStream, "");
			}
			logger.info("压缩文件成功：" + zipFileName);
		} catch (Exception e) {
			logger.error("异常", e);
		} finally {
			if (zipOutputStream != null) {
				try {
					zipOutputStream.close();
				} catch (IOException e) {
					logger.error("jarOutput.close()异常", e);
				}
			}
		}
		return null;
	}

	/**
	 * @param file
	 *            压缩文件
	 * @param zipOutputStream
	 *            ZipOutputStream
	 * @param pathName
	 *            相对路径
	 * @throws Exception
	 *             异常
	 */
	private static void zipFiles(File file, ZipOutputStream zipOutputStream, String pathName) throws Exception {
		String fileName = pathName + file.getName();
		if (file.isDirectory()) {
			fileName = fileName + "/";
			zipOutputStream.putNextEntry(new ZipEntry(fileName));
			String fileNames[] = file.list();
			if (fileNames != null) {
				for (int i = 0; i < fileNames.length; i++) {
					zipFiles(new File(file, fileNames[i]), zipOutputStream, fileName);
				}
				zipOutputStream.closeEntry();
			}
		} else {
			ZipEntry zipEntry = new ZipEntry(fileName);
			BufferedInputStream in = new BufferedInputStream(new FileInputStream(file));
			zipOutputStream.putNextEntry(zipEntry);

			byte[] buf = new byte[bufSize];
			int len;
			while ((len = in.read(buf)) >= 0) {
				zipOutputStream.write(buf, 0, len);
			}
			in.close();
			zipOutputStream.closeEntry();
		}
	}

	/**
	 * 压缩文件夹内的所有文件和目录。
	 * 
	 * @param zipDirectory
	 *            需要压缩的文件夹名
	 * @return 成功返回null，否则返回失败信息
	 */
	public static String antzip(String zipDirectory) {
		File zipDir = new File(zipDirectory);
		return antzip(zipDirectory, zipDir.getPath(), false);
	}

	/**
	 * 压缩文件夹内的所有文件和目录。
	 * 
	 * @param zipDirectory
	 *            需要压缩的文件夹名
	 * @param zipFileName
	 *            压缩后的zip文件名，如果后缀不是".zip, .jar, .war"， 自动添加后缀".zip"。
	 * @param includeSelfDir
	 *            是否包含自身文件夹
	 * @return 成功返回null，否则返回失败信息
	 */
	public static String antzip(String zipDirectory, String zipFileName, boolean includeSelfDir) {
		File zipDir = new File(zipDirectory);
		File[] willZipFileArr;
		if (includeSelfDir || zipDir.isFile()) {
			willZipFileArr = new File[] { zipDir };
		} else {
			willZipFileArr = zipDir.listFiles();
		}
		return antzip(willZipFileArr, zipFileName);
	}

	/**
	 * 压缩多个文件或目录。可以指定多个单独的文件或目录。
	 * 
	 * @param files
	 *            要压缩的文件或目录组成的<code>File</code>数组。
	 * @param zipFileName
	 *            压缩后的zip文件名，如果后缀不是".zip, .jar, .war"，自动添加后缀".zip"。
	 * @return 成功返回null，否则返回失败信息
	 */
	public static String antzip(File[] files, String zipFileName) {
		// 未指定压缩文件名，默认为"ZipFile"
		if (zipFileName == null || zipFileName.equals(""))
			zipFileName = "ZipFile";

		// 添加".zip"后缀
		if (!zipFileName.toLowerCase().endsWith(".zip") && !zipFileName.toLowerCase().endsWith(".jar") && !zipFileName.toLowerCase().endsWith(".war")) {
			zipFileName += ".zip";
		}

		ZipOutputStream zipOutput = null;
		try {
			zipOutput = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(zipFileName)));

			for (File file : files) {
				antzipFiles(file, zipOutput, "");
			}

			logger.info("压缩文件成功：" + zipFileName);
		} catch (Exception e) {
			logger.error("压缩文件异常", e);
			return e.getMessage();
		} finally {
			try {
				assert zipOutput != null;
				zipOutput.close();
			} catch (Exception e) {
				logger.error("异常", e);
			}
		}
		return null;
	}

	/**
	 * @param file
	 *            压缩文件
	 * @param zipOutput
	 *            ZipOutputStream
	 * @param pathName
	 *            相对路径
	 * @throws Exception
	 *             异常
	 */
	private static void antzipFiles(File file, ZipOutputStream zipOutput, String pathName) throws Exception {
		String fileName = pathName + file.getName();
		if (file.isDirectory()) {
			fileName = fileName + "/";
			zipOutput.putNextEntry(new ZipEntry(fileName));
			String fileNames[] = file.list();
			if (fileNames != null) {
				for (int i = 0; i < fileNames.length; i++) {
					antzipFiles(new File(file, fileNames[i]), zipOutput, fileName);
				}
				zipOutput.closeEntry();
			}
		} else {
			ZipEntry jarEntry = new ZipEntry(fileName);
			BufferedInputStream in = new BufferedInputStream(new FileInputStream(file));
			zipOutput.putNextEntry(jarEntry);

			byte[] buf = new byte[bufSize];
			int len;
			while ((len = in.read(buf)) >= 0) {
				zipOutput.write(buf, 0, len);
			}
			in.close();
			zipOutput.closeEntry();
		}
	}

	/**
	 * 解压指定zip文件。
	 * 
	 * @param unZipFile
	 *            需要解压的zip文件对象
	 * @return 成功返回null，否则返回失败信息
	 */
	public static String unZip(File unZipFile) {
		return unZip(unZipFile.getPath(), null);
	}

	/**
	 * 解压指定zip文件到指定的目录。
	 * 
	 * @param unZipFile
	 *            需要解压的zip文件对象
	 * @param destPath
	 *            解压目的目录
	 * @return 成功返回null，否则返回失败信息
	 */
	public static String unZip(File unZipFile, String destPath) {
		return unZip(unZipFile.getPath(), destPath);
	}

	/**
	 * 解压指定zip文件。
	 * 
	 * @param unZipFileName
	 *            需要解压的zip文件名
	 * @return 成功返回null，否则返回失败信息
	 */
	public static String unZip(String unZipFile) {
		return unZip(unZipFile, null);
	}

	/**
	 * 解压指定zip文件到指定的目录。
	 * 
	 * @param unZipFileName
	 *            需要解压的zip文件名
	 * @param destPath
	 *            解压目的目录，如果为null则为当前zip文件所有目录
	 * @return 成功返回null，否则返回失败信息
	 */
	public static String unZip(String unZipFile, String destPath) {
		File unzipFile = new File(unZipFile);// 压缩文件

		if (destPath == null || destPath.trim().length() == 0) {
			destPath = unzipFile.getParent();
		}

		File destFile;
		ZipFile zipFile = null;
		try {
			zipFile = new ZipFile(unzipFile);// 实例化ZipFile，每一个zip压缩文件都可以表示为一个ZipFile
			Enumeration<? extends ZipEntry> enumeration = zipFile.entries();
			while (enumeration.hasMoreElements()) {
				ZipEntry zipEntry = (ZipEntry) enumeration.nextElement();
				destFile = new File(destPath + File.separator + zipEntry.getName());
				unZipFile(destFile, zipFile, zipEntry);// 执行解压
			}
		} catch (Exception e) {
			logger.error("解压ZIP文件异常", e);
			return e.getMessage();
		} finally {
			try {
				assert zipFile != null;
				zipFile.close();
			} catch (Exception e) {
				logger.error("异常", e);
			}
		}
		return null;
	}

	/* 执行解压 */
	private static void unZipFile(File destFile, ZipFile zipFile, ZipEntry zipEntry) throws Exception {
		InputStream inputStream = null;
		FileOutputStream fileOutputStream = null;
		try {
			if (zipEntry.isDirectory()) // 是目录，则创建目录
			{
				destFile.mkdirs();
			} else // 是文件
			{
				// 如果指定文件的父目录不存在,则创建之.
				File parent = destFile.getParentFile();
				if (parent != null && !parent.exists()) {
					parent.mkdirs();
				}

				inputStream = zipFile.getInputStream(zipEntry);

				fileOutputStream = new FileOutputStream(destFile);
				byte[] buf = new byte[bufSize];
				int readedBytes;
				while ((readedBytes = inputStream.read(buf)) > 0) {
					fileOutputStream.write(buf, 0, readedBytes);
				}
			}
		} catch (Exception e) {
			logger.error("执行解压异常", e);
			throw new Exception("执行解压异常", e);
		} finally {
			try {
				if (null != inputStream) {
					inputStream.close();
				}
				if (null != fileOutputStream) {
					fileOutputStream.close();
				}
			} catch (Exception ee) {
				logger.error("异常", ee);
			}
		}
	}
}
